/**
 * LY.com Inc.
 * Copyright (c) 2004-2018 All Rights Reserved.
 */
package com.gitee.kamismile.gateway.component.route;

import com.gitee.kamismile.gateway.component.config.GatewayConstant;
import com.gitee.kamismile.stone.commmon.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static java.util.Collections.synchronizedMap;

/**
 * @author dong.li
 * @version $Id: RedisRouteDefinitionRepository, v 0.1 2018/10/18 10:06 dong.li Exp $
 */
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {

    private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());

    @Autowired
    GatewayConstant gatewayConstant;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
//        return super.getRouteDefinitions().switchIfEmpty(Flux.defer(() -> {
//            List<RouteDefinition> routeDefinitions = new ArrayList<>();
//            redisTemplate.opsForHash().values(gatewayConstant.getGatewayRoutes()).forEach(routeDefinition -> {
//                RouteDefinition route = JsonUtil.fromJson(routeDefinition.toString(), RouteDefinition.class);
//                routeDefinitions.add(route);
//                super.save(Mono.just(route)).subscribe();
//            });
//            return Flux.fromIterable(routeDefinitions);
//        }));

        if (!routes.isEmpty()) {
            return Flux.fromIterable(routes.values())
                    .subscribeOn(Schedulers.boundedElastic());
        }

        List<RouteDefinition> routeDefinitions = new ArrayList<>();
        redisTemplate.opsForHash().values(gatewayConstant.getGatewayRoutes()).forEach(routeDefinition -> {
            RouteDefinition routeDef = JsonUtil.fromJson(routeDefinition.toString(), RouteDefinition.class);
            routeDefinitions.add(routeDef);
            routes.put(routeDef.getId(), routeDef);
        });
        return Flux.fromIterable(routeDefinitions)
                .subscribeOn(Schedulers.boundedElastic());
    }

    public Map<String, RouteDefinition> getRoutes() {
        return routes;
    }

    //    @Override
//    public Mono<Void> save(Mono<RouteDefinition> route) {
//        return route
//                .flatMap(routeDefinition -> {
//                    redisTemplate.opsForHash().put(gatewayConstant.getGatewayRoutes(), routeDefinition.getId(),
//                            JsonUtil.toJson(routeDefinition));
////                    super.save(route).subscribe();
//                    return Mono.empty();
//                });
//    }
//
//    @Override
//    public Mono<Void> delete(Mono<String> routeId) {
//        return routeId.flatMap(id -> {
//            if (redisTemplate.opsForHash().hasKey(gatewayConstant.getGatewayRoutes(), id)) {
//                redisTemplate.opsForHash().delete(gatewayConstant.getGatewayRoutes(), id);
////                super.delete(routeId).subscribe();
//                return Mono.empty();
//            }
//            return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
//        });
//    }


    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(r -> {
            routes.put(r.getId(), r);
            return Mono.empty();
        });
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            if (routes.containsKey(id)) {
                routes.remove(id);
                return Mono.empty();
            }
            return Mono.defer(() -> Mono.error(
                    new NotFoundException("RouteDefinition not found: " + routeId)))
                    ;
        });
    }

}
